package ru.spb.osll.myfit.server.handler;

import org.apache.log4j.Logger;
import ru.spb.osll.myfit.common.*;
import ru.spb.osll.myfit.server.parser.PageUtils;
import ru.spb.osll.myfit.server.parser.RecursiveIncludeException;
import ru.spb.osll.myfit.server.parser.TableElementParser;

import java.io.IOException;
import java.util.*;

/**
 * @author kdv
 * 
 */
public class ContentHandler implements Handler {
  private final static Logger log = Logger.getLogger(ContentHandler.class);
  private PageLoader m_pageLoader;
  private TableElementParser m_tableParser = new TableElementParser();
  private final String m_root;
  private ContentCreator m_contentCreator;// = new ContentCreator();
  private ParentVariablesCreator m_parentVariablesCreator;

  public ContentHandler(String root, PageLoader pageLoader,
                        ContentCreator contentCreator,
                        ParentVariablesCreator parentVariablesCreator) {
    m_root = root;
    m_pageLoader = pageLoader;
    m_contentCreator = contentCreator;
    m_parentVariablesCreator = parentVariablesCreator;
  }

  @Override
  public Table getTable(Vector<String> rawTable, TableStyle tableStyle) {
    Table table = m_tableParser.parseTable(rawTable);
    table.setStyle(tableStyle);
    return table;
  }

  @Override
  public Page loadPage(String pagePath) {
    return loadPageFromDirectory(pagePath, m_root);
  }

  @Override
  public Page loadPageFromDirectory(String pageName, String fromRoot) {
    Page page = null;
    try {
      page = m_pageLoader.loadPage(PageUtils.getFullPath(fromRoot, pageName));
      LinkedList<String> list = new LinkedList<String>();
      list.add(pageName);
      try {
        handlePage(fromRoot, page, list, false);
      } catch (RecursiveIncludeException e) {
      }
    } catch (IOException e) {
      log.debug("Not found Page: " + pageName + ", from " + fromRoot);
      page = new Page();
    }
    addSetUpAndTearDown(page, pageName, fromRoot);

    HashMap<String, String> map = m_parentVariablesCreator
        .getParentVariablesMap(PageUtils.getFullPath(fromRoot, pageName),
            fromRoot);

    if (map != null) {
      page.setVariables(map);
    }

    return page;
  }

  @Override
  public PageElement loadInclude(String includePagePath, String parentPagePath) {
    try {
      Include include = new Include();
      include.setPath(includePagePath);
      LinkedList<String> allPaths = new LinkedList<String>();
      allPaths.add(parentPagePath);
      handleInclude(m_root, include, allPaths);
      return include;
    } catch (IOException e) {
      return new IncludeError(includePagePath,
          createPageWithError(e.getMessage()));
    } catch (RecursiveIncludeException e) {
      String message = TextParsingTools.RECURSIVE_CALL + parentPagePath +
          " from the page " + e.getMessage();
      return new IncludeError(includePagePath, createPageWithError(message));
    }
  }

  @Override
  public Content createContent(String root, String pagePath, int level) {
    return m_contentCreator.createContent(root, pagePath, level);
  }

  private void handlePage(String root, Page page, LinkedList<String> allPaths,
                          boolean isIncludedPage)
      throws RecursiveIncludeException {
    ListIterator<PageElement> iterator = page.getElements().listIterator();

    while (iterator.hasNext()) {
      PageElement pageElement = iterator.next();
      PageElementType type = pageElement.getType();
      switch (type) {

      case INCLUDE:
        Include include = (Include) pageElement;
        try {
          handleInclude(root, include, allPaths);
        } catch (RecursiveIncludeException e) {
          if (isIncludedPage) {
            throw e;
          } else {
            String message = TextParsingTools.RECURSIVE_CALL +
                allPaths.getFirst() + " from the page " + e.getMessage();
            iterator.set(new IncludeError(include.getPath(),
                createPageWithError(message)));

          }

        } catch (IOException e) {
          iterator.set(new IncludeError(include.getPath(),
              createPageWithError(e.getMessage())));
        }

        break;
      case CONTENT:
        Content content = createContent(root, allPaths.getLast(),
            ((Content) pageElement).getLevel());
        iterator.set(content);
        break;
      default:
        break;
      }
    }
  }

  private void handleInclude(String root, Include include,
                             LinkedList<String> allPaths)
      throws RecursiveIncludeException, IOException {
    String includePath = include.getPath();
    if (isRecursiveInclude(includePath, allPaths)) {
      allPaths.add(includePath);
      throw new RecursiveIncludeException(getRecursivePath(allPaths));
    } else {
      Page page = m_pageLoader.loadPage(PageUtils
          .getFullPath(root, includePath));
      allPaths.add(includePath);
      // page.setVariables(null);
      page.setVariables(new HashMap<String, String>());
      handlePage(root, page, allPaths, true);
      include.setPage(page);

    }
  }

  private void addSetUpAndTearDown(Page page, String pagePath, String root) {
    PageElement setUp = getSetUpOrTearDown(root, pagePath,
        TextParsingTools.SETUP);
    if (setUp != null) {
      page.insertElement(setUp, 0);
    }

    PageElement tearDown = getSetUpOrTearDown(root, pagePath,
        TextParsingTools.TEARDOWN);
    if (tearDown != null) {
      page.insert(tearDown);
    }

  }

  private PageElement getSetUpOrTearDown(String root, String pagePath,
                                         String SETUP_OR_TEARDOWN) {
    if (isSetUpOrTearDown(pagePath)) {
      return null;
    }

    String includePath;
    if (pagePath.contains(".")) {
      int beginIndex = pagePath.lastIndexOf(".");
      String rootPath = pagePath.substring(0, beginIndex + 1);
      includePath = rootPath + SETUP_OR_TEARDOWN;
      if (!includePath.startsWith(".")) {
        includePath = "." + includePath;
      }
    } else {
      includePath = "." + SETUP_OR_TEARDOWN;
    }

    try {
      Page page = m_pageLoader.loadPage(PageUtils
          .getFullPath(root, includePath));
      LinkedList<String> allPaths = new LinkedList<String>();
      allPaths.add(includePath);

      try {
        handlePage(root, page, allPaths, false);
      } catch (RecursiveIncludeException e) {

      }

      if (SETUP_OR_TEARDOWN.equals(TextParsingTools.SETUP)) {
        return new SetUp(includePath, page);
      } else {
        return new TearDown(includePath, page);

      }

    } catch (IOException e) {
      log.debug(SETUP_OR_TEARDOWN + " for page " + pagePath + " not found");
    }
    return null;
  }

  private boolean isSetUpOrTearDown(String pagePath) {
    String path;
    if (pagePath.contains(".")) {
      int beginIndex = pagePath.lastIndexOf(".");
      path = pagePath.substring(beginIndex + 1);
    } else {
      path = pagePath;
    }

    if (path.equals(TextParsingTools.SETUP)) {
      return true;
    }
    if (path.equals(TextParsingTools.TEARDOWN)) {
      return true;
    }

    return false;
  }

  private boolean isRecursiveInclude(String includePath, List<String> allPaths) {
    if (allPaths.contains(includePath)) {
      return true;
    }

    if (includePath.startsWith(".")) {
      String pathWithoutPoint = includePath.substring(1);
      if (allPaths.contains(pathWithoutPoint)) {
        return true;
      }
    } else {
      String pathWithPoint = "." + includePath;
      if (allPaths.contains(pathWithPoint)) {
        return true;
      }
    }
    return false;
  }

  private String getRecursivePath(List<String> allPath) {
    String result = new String();
    int size = allPath.size();
    for (int i = 1; i < size; ++i) {
      result += allPath.get(i);
      result += (i != size - 1) ? " ⇾ " : " ";
    }
    return result;
  }

  private Page createPageWithError(String errorMessage) {
    Page result = new Page();
    result.insert(createPageError(errorMessage));

    return result;
  }

  private PageError createPageError(String msg) {
    PageError pageError = new PageError();
    pageError.setMessage(msg);
    return pageError;
  }

}
